/*
    This file is part of Slydini.

    Slydini is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Slydini is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Slydini.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "mouse.h"

#ifdef LINUX

#include <cstdio>

Mouse::Mouse(int screen) {
	this->screen = screen;
	display=XOpenDisplay(NULL);
	root = DefaultRootWindow(display);
	desktopWidth = XDisplayWidth(display,screen);
	desktopHeight = XDisplayHeight(display,screen);
	isDown = false;
}

void Mouse::placeMouse(int x, int y) {
	XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);
	//Window ret_child, ret_root;
	//int tx,ty,dx,dy;
	//unsigned int dm;
	//XQueryPointer(display, root, &ret_root, &ret_child, &tx, &ty, &dx, &dy, &dm);

}

void Mouse::moveMouse(int x, int y) {
	if (y == 0)
		return;
	printf("place: %d %d\n",x,y);
	CvPoint n = calculateNewPosition(x,y);
	printf("place2: %d %d\n",n.x,n.y);
	placeMouse(n.x,n.y);
}

void Mouse::mouseClick(int button, int mode) {
	Display *display=XOpenDisplay(NULL);
	XEvent event;
	
	memset(&event, 0x00, sizeof(event));
	
	event.type = ButtonPress;
	event.xbutton.button = button;
	event.xbutton.same_screen = True;
	
	XQueryPointer(display, RootWindow(display, DefaultScreen(display)), &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
	
	event.xbutton.subwindow = event.xbutton.window;
	
	while(event.xbutton.subwindow) {
		event.xbutton.window = event.xbutton.subwindow;
		XQueryPointer(display, event.xbutton.window, &event.xbutton.root, &event.xbutton.subwindow, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
	}
	
	if (mode==0 || mode==1) {
		XSendEvent(display, PointerWindow, True, 0xfff, &event);
		XFlush(display);
	}
	
	if (mode==0 || mode==2) {
		event.type = ButtonRelease;
		event.xbutton.state = 0x100;
		XSendEvent(display, PointerWindow, True, 0xfff, &event);
		XFlush(display);
	}	
	XCloseDisplay(display);
}


#else


Mouse::Mouse(int screen) {
	this->screen = screen;
	QWidget *d = QApplication::desktop();
	desktopWidth = d->width();
	desktopHeight = d->height();
	isDown = false;
}

void Mouse::placeMouse(int x, int y) {
	 SetCursorPos(desktopWidth-x,y);

}

void Mouse::moveMouse(int x, int y) {
	if (y == 0)
		return;
//	printf("place: %d %d\n",x,y);
	CvPoint n = calculateNewPosition(x,y);
//	printf("place2: %d %d\n",n.x,n.y);
	placeMouse(n.x,n.y);
}

void Mouse::mouseClick(int button, int mode) {
	 if (mode == 0 || mode == 1)
	 	 mouse_event(MOUSEEVENTF_LEFTDOWN, lastX, lastY, 0, 0);
	 if (mode == 0 || mode == 2)
	 mouse_event(MOUSEEVENTF_LEFTUP, lastX, lastY, 0, 0);
}



#endif

CvPoint Mouse::calculateNewPosition(int x, int y) {
	CvPoint result;
	if (minx==-1) {
		minx = x;
		miny = y;
		maxx = x;
		maxy = y;
	}

	if (x<minx)
		minx = x;
	if (x>maxx)
		maxx = x;
	if (y<miny)
		miny = y;
	if (y>maxy)
		maxy = y;

	if (abs(minx-maxx)<30 || abs(miny-maxy)<30) {
		CvPoint result;
		result.x = desktopWidth/2;
		result.y = desktopHeight/2;
		return result;
	}

	if ((x-minx)>0.85*(maxx-minx) && boxX < desktopWidth) {
//		boxX += (x-minx)-0.85*(maxx-minx);
		boxX += (x-minx)-(int)(0.85*(maxx-minx));
	}

	if ((x-minx)<0.15*(maxx-minx) && boxX > 0) {
//		boxX -= 0.15*(maxx-minx)-(x-minx);
		boxX -= (int)(0.15*(maxx-minx))-(x-minx);
	}

	if ((y-miny)>0.85*(maxy-miny) && boxY < desktopHeight) {
//		boxY += (y-miny)-0.85*(maxy-miny);
		boxY += (y-miny)-(int)(0.85*(maxy-miny));
	}

	if ((y-miny)<0.15*(maxy-miny) && boxY > 0) {
//		boxY -= (int)(0.15*(maxy-miny))-(y-miny);
		boxY -= 0.15*(maxy-miny)-(y-miny);
	}

	result.x = boxX-(maxx-minx)/2+x-minx;
	result.y = boxY-(maxy-miny)/2+y-miny;
	lastX = result.x;
	lastY = result.y;


//	printf("%d (%d-%d) = %d\n",boxX, minx, maxx, result.x);
//	printf("%d (%d-%d) = %d\n",boxY, miny, maxy, result.y);

	return result;



}

/*CvPoint Mouse::calculateNewPosition(int x, int y, int frame_count) {
  if (minx==-1) {
  minx = x;
  miny = y;
  maxx = x;
  maxy = y;
  }

  if (x<minx)
  minx = x;
  if (x>maxx)
  maxx = x;
  if (y<miny)
  miny = y;
  if (y>maxy)
  maxy = y;

  x = (int)((double)(x-minx)/(maxx-minx)*desktopWidth);
  y = (int)((double)(y-miny)/(maxy-miny)*desktopHeight);

  CvPoint point;
  point.x = x;
  point.y = y;
  if (!lastPoints.size()) {
  lastPoints.push_back(point);
  return point;
  }
//	printf("here %d\n",lastPoints.size());

//	int diffx = fabs(lastPoints[lastPoints.size()-1].x-x);
//	int diffy = fabs(lastPoints[lastPoints.size()-1].y-y);
//	printf("pp: %d %d\n",diffx,diffy);

//	if (diffx*diffx+diffy*diffy<MOUSE_POS_DIFF) {
lastPoints.push_back(point);
lastUpdate = frame_count;
//	}
vector<CvPoint>::iterator J;
for (J=lastPoints.begin(); J!=lastPoints.end(); J++)
printf("p: %d %d\n",J->x,J->y);

if (lastPoints.size()<4)
return point;

vector<CvPoint>::iterator I = lastPoints.begin();
if (lastPoints.size()>4)
lastPoints.erase(I);

//	printf("here0\n");
if (lastPoints.size() != 4)
return point;
double t = (frame_count - lastUpdate) / (double)TRAVEL_TIME;
if (t>1)
return point;
//	printf("here2\n");
int newx, newy;
newx = (int)(((-t*t*t+3*t*t-3*t+1)/6)*lastPoints[0].x + ((3*t*t*t-6*t*t+4)/6)*lastPoints[1].x + ((-3*t*t*t+3*t*t+3*t+1)/6)*lastPoints[2].x + ((t*t*t)/6)*lastPoints[3].x);
newy = (int)(((-t*t*t+3*t*t-3*t+1)/6)*lastPoints[0].y + ((3*t*t*t-6*t*t+4)/6)*lastPoints[1].y + ((-3*t*t*t+3*t*t+3*t+1)/6)*lastPoints[2].y + ((t*t*t)/6)*lastPoints[3].y);

CvPoint result;
result.x = newx;
result.y = newy;
return result;

}*/

void Mouse::reset() {
	minx = miny = maxx = maxy = -1;
	placeMouse(desktopWidth/2,desktopHeight/2);
	boxX = desktopWidth/2;
	boxY = desktopHeight/2;

}

int Mouse::getDesktopWidth() {
	return desktopWidth;
}

int Mouse::getDesktopHeight() {
	return desktopHeight;
}

void Mouse::mouseDown() {
	mouseClick(1,1);
}

void Mouse::mouseUp() {
	mouseClick(1,2);

}

void Mouse::click() {
	mouseClick(1,0);
}
